#include <gtest/gtest.h>
#include "protocol/JsonRpcMessageSerializer.h"
#include "JsonRpcErrorCode.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QVariant>

using namespace RpcError;

/**
 * @brief Test class for JsonRpcMessageSerializer
 */
class TestJsonRpcMessageSerializer : public ::testing::Test
{
protected:
    void SetUp() override
    {
        mSerializer = std::make_unique<JsonRpcMessageSerializer>();
    }
    
    void TearDown() override
    {
        mSerializer.reset();
    }
    
    std::unique_ptr<JsonRpcMessageSerializer> mSerializer;
};

TEST_F(TestJsonRpcMessageSerializer, SerializeSuccessResponseWithStringId)
{
    QVariant id("request-123");
    QJsonObject result{{"value", "test"}};
    
    QJsonObject response = mSerializer->serializeSuccessResponse(id, result);
    
    EXPECT_EQ(response["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(response["id"].toString(), "request-123");
    EXPECT_TRUE(response.contains("result"));
    EXPECT_FALSE(response.contains("error"));
    
    QJsonObject resultObj = response["result"].toObject();
    EXPECT_EQ(resultObj["value"].toString(), "test");
}

TEST_F(TestJsonRpcMessageSerializer, SerializeSuccessResponseWithNumericId)
{
    QVariant id(456);
    QJsonObject result{{"values", QJsonArray{1, 2, 3}}};
    
    QJsonObject response = mSerializer->serializeSuccessResponse(id, result);
    
    EXPECT_EQ(response["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(response["id"].toInt(), 456);
    EXPECT_TRUE(response.contains("result"));
    EXPECT_FALSE(response.contains("error"));
    
    QJsonObject resultObj = response["result"].toObject();
    QJsonArray resultArray = resultObj["values"].toArray();
    EXPECT_EQ(resultArray.size(), 3);
    EXPECT_EQ(resultArray[0].toInt(), 1);
}

TEST_F(TestJsonRpcMessageSerializer, SerializeErrorResponseWithStandardErrorCode)
{
    QVariant id("error-request");
    
    QJsonObject response = mSerializer->serializeErrorResponse(id, METHOD_NOT_FOUND);
    
    EXPECT_EQ(response["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(response["id"].toString(), "error-request");
    EXPECT_FALSE(response.contains("result"));
    EXPECT_TRUE(response.contains("error"));
    
    QJsonObject error = response["error"].toObject();
    EXPECT_EQ(error["code"].toInt(), METHOD_NOT_FOUND);
    EXPECT_EQ(error["message"].toString(), "Method not found, method does not exist or invalid");
    EXPECT_FALSE(error.contains("data")); // No data provided
}

TEST_F(TestJsonRpcMessageSerializer, SerializeErrorResponseWithErrorData)
{
    QVariant id(999);
    QJsonValue errorData("Additional error information");
    
    QJsonObject response = mSerializer->serializeErrorResponse(id, INVALID_PARAMS, errorData);
    
    EXPECT_EQ(response["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(response["id"].toInt(), 999);
    EXPECT_FALSE(response.contains("result"));
    EXPECT_TRUE(response.contains("error"));
    
    QJsonObject error = response["error"].toObject();
    EXPECT_EQ(error["code"].toInt(), INVALID_PARAMS);
    EXPECT_EQ(error["message"].toString(), "Invalid argument. The supplied argument is invalid");
    EXPECT_TRUE(error.contains("data"));
    
    EXPECT_EQ(error["data"].toString(), "Additional error information");
}

TEST_F(TestJsonRpcMessageSerializer, SerializeErrorResponseWithCustomErrorCode)
{
    QVariant id("custom-error");
    int customErrorCode = -32001; // Custom server error
    
    QJsonObject response = mSerializer->serializeErrorResponse(id, customErrorCode);
    
    EXPECT_EQ(response["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(response["id"].toString(), "custom-error");
    EXPECT_FALSE(response.contains("result"));
    EXPECT_TRUE(response.contains("error"));
    
    QJsonObject error = response["error"].toObject();
    EXPECT_EQ(error["code"].toInt(), customErrorCode);
    EXPECT_EQ(error["message"].toString(), "Internal server error");
}

TEST_F(TestJsonRpcMessageSerializer, SerializeNotificationWithParams)
{
    QString method("testNotification");
    QJsonObject params{{"param1", "value1"}, {"param2", 42}};
    
    QJsonObject notification = mSerializer->serializeNotification(method, params);
    
    EXPECT_EQ(notification["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(notification["method"].toString(), "notifications/testNotification");
    EXPECT_TRUE(notification.contains("params"));
    EXPECT_FALSE(notification.contains("id")); // Notifications should not have id
    
    QJsonObject paramsObj = notification["params"].toObject();
    EXPECT_EQ(paramsObj["param1"].toString(), "value1");
    EXPECT_EQ(paramsObj["param2"].toInt(), 42);
}

TEST_F(TestJsonRpcMessageSerializer, SerializeNotificationWithoutParams)
{
    QString method("simpleNotification");
    
    QJsonObject notification = mSerializer->serializeNotification(method, QJsonObject());
    
    EXPECT_EQ(notification["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(notification["method"].toString(), "notifications/simpleNotification");
    EXPECT_FALSE(notification.contains("params")); // No params should be included
    EXPECT_FALSE(notification.contains("id"));
}

TEST_F(TestJsonRpcMessageSerializer, SerializeNotificationWithArrayParams)
{
    QString method("arrayNotification");
    QJsonObject params{{"items", QJsonArray{"item1", "item2", "item3"}}};
    
    QJsonObject notification = mSerializer->serializeNotification(method, params);
    
    EXPECT_EQ(notification["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(notification["method"].toString(), "notifications/arrayNotification");
    EXPECT_TRUE(notification.contains("params"));
    
    QJsonObject paramsObj = notification["params"].toObject();
    QJsonArray paramsArray = paramsObj["items"].toArray();
    EXPECT_EQ(paramsArray.size(), 3);
    EXPECT_EQ(paramsArray[0].toString(), "item1");
}

TEST_F(TestJsonRpcMessageSerializer, ToByteArrayCompactJson)
{
    QJsonObject testObject{
        {"jsonrpc", "2.0"},
        {"id", "test-id"},
        {"result", QJsonObject{{"value", "test"}}}
    };
    
    QByteArray byteArray = mSerializer->toByteArray(testObject);
    
    EXPECT_FALSE(byteArray.isEmpty());
    
    // Should be compact JSON (no whitespace)
    // Due to Qt 5.12.8 using QHash internally, field order may vary
    // Parse back to JSON object to verify content instead of exact string match
    QJsonDocument doc = QJsonDocument::fromJson(byteArray);
    ASSERT_FALSE(doc.isNull());
    ASSERT_TRUE(doc.isObject());
    
    QJsonObject resultObject = doc.object();
    EXPECT_EQ(resultObject["jsonrpc"].toString(), "2.0");
    EXPECT_EQ(resultObject["id"].toString(), "test-id");
    EXPECT_TRUE(resultObject.contains("result"));
    
    QJsonObject result = resultObject["result"].toObject();
    EXPECT_EQ(result["value"].toString(), "test");
}
